#include <string.h>
#include <stdio.h>
#include "mhscpu.h"

// #define TEST_ON_FPGA


typedef struct
{
    uint32_t PWM_HZ;
    uint32_t DutyCycleHighPluse;
    uint32_t DutyCycleLowPluse;
    SYSCTRL_ClocksTypeDef clocks;
    TIM_PWMInitTypeDef TIM_PWMSetStruct;
} timer_pwm_set;


typedef struct
{
    uint8_t PinName[5];
    GPIO_TypeDef *GPIOx;
    uint16_t RampPin;
} TypeRampPinDef;


typedef struct
{
    uint8_t PwmName[5];
    TypeRampPinDef RampPin[4];
} Type_RampDef;

extern void UART_Configuration (void);

void NVIC_Configuration(void);
void UART_Configuration(void);
void TIMER_Configuration(void);
void PrintfTimerRemapList(void);

void TimerPWMSetStructInit(void);
void TIMER_PWMSet(void);
void PrintSet(void);
void PrintList(void);
void UART0_Set(void);
void PWM_Pin_Set(void);
void PrintfCurrentTimerRemap(void);

uint16_t PWM_CurrentPin = 1;

Type_RampDef Ramp_List[] =
{
    {"PWM0", "PA0", NULL,  GPIO_Pin_0, "PB0", GPIOB, GPIO_Pin_0, "---",   NULL, NULL,  "---",  NULL,       NULL},
    {"PWM1", "PA1", NULL,  GPIO_Pin_1, "PB1", GPIOB, GPIO_Pin_1, "---",   NULL, NULL,  "---",  NULL,       NULL},
    {"PWM2", "PA2", GPIOA, GPIO_Pin_2, "PB2", GPIOB, GPIO_Pin_2, "---",   NULL, NULL,  "---",  NULL,       NULL},
    {"PWM3", "PA3", GPIOA, GPIO_Pin_3, "PB3", GPIOB, GPIO_Pin_3, "---",   NULL, NULL,  "---",  NULL,       NULL},
    {"PWM4", "PA4", GPIOA, GPIO_Pin_4, "PB4", GPIOB, GPIO_Pin_4, "---",   NULL, NULL,  "---",  NULL,       NULL},
    {"PWM5", "PA5", GPIOA, GPIO_Pin_5, "PB5", GPIOB, GPIO_Pin_5, "---",   NULL, NULL,  "---",  NULL,       NULL},
};



timer_pwm_set timerPWMSet;

int main(void)
{
    SYSCTRL_PLLConfig(SYSCTRL_PLL_96MHz);
    SYSCTRL_HCLKConfig(SYSCTRL_HCLK_Div_None);
    SYSCTRL_PCLKConfig(SYSCTRL_PCLK_Div2);

    SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_UART0 | SYSCTRL_APBPeriph_TIMM0 |\
                              SYSCTRL_APBPeriph_GPIO,ENABLE);
    SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_UART0 | SYSCTRL_APBPeriph_TIMM0,ENABLE);

    UART_Configuration();

    TimerPWMSetStructInit();
    TIMER_Configuration();
    NVIC_Configuration();

    printf("MegaHunt SCPU Timer PWM Demo V1.0.\r\n");
    PrintSet();
    PrintList();

    while(1)
    {
        UART0_Set();
    }
}

void PrintList()
{
    printf("\r\n\r\n");
    printf("============Option List===========\n");
    \
    printf("\"s\" Set PWM Pin\n");
    printf("\">\" Inc PWM Width 1KHZ(Max 10MZ)\n");
    printf("\"<\" Dec PWM Width 1KHZ(Min 1KZ)\n");
    printf("\"+\" Inc PWM HighPeriod DutyCycle\n");
    printf("\"-\" Dec PWM HighPeriod DutyCycle\n");
    printf("\"0\" Timer0 Enable or Disable\n");
    printf("\"1\" Timer1 Enable or Disable\n");
    printf("\"2\" Timer2 Enable or Disable\n");
    printf("\"3\" Timer3 Enable or Disable\n");
    printf("\"4\" Timer4 Enable or Disable\n");
    printf("\"5\" Timer5 Enable or Disable\n");
    printf("ESC return to the previous screen\n");
    printf("============ List  End ===========\n");
}

void PrintSet(void)
{
    printf("PWM Width %dKHZ\n", timerPWMSet.PWM_HZ / 1000);
    printf("DutyCycle HighPluse : LowPluse = %d\n", timerPWMSet.DutyCycleHighPluse);
}

void UART0_Set(void)
{
    uint8_t cmd = 0;
    if (UART_GetLineStatus(UART0) & UART_LINE_STATUS_RX_RECVD)
    {
        cmd = UART_ReceiveData(UART0);
        switch(cmd)
        {
        case 's':
        {
            PrintfTimerRemapList();
            PWM_Pin_Set();
        }
        break;
        //'>'
        case '>':
        {
            if (timerPWMSet.PWM_HZ < 10000000)
            {
                timerPWMSet.PWM_HZ += 1000;
                TIMER_PWMSet();
                printf("PWM_HZ = %d\r\n", timerPWMSet.PWM_HZ);
            }
        }
        break;

        //'<'
        case '<':
        {
            if (timerPWMSet.PWM_HZ > 1000)
            {
                timerPWMSet.PWM_HZ -= 1000;
                TIMER_PWMSet();
                printf("PWM_HZ = %d\r\n", timerPWMSet.PWM_HZ);
            }
        }
        break;
        //'+'
        case '+':
        {
            timerPWMSet.DutyCycleHighPluse ++;
            TIMER_PWMSet();
            printf("DutyCycle = %d\r\n", timerPWMSet.DutyCycleHighPluse);
        }
        break;
        //'-'
        case '-':
        {
            if (timerPWMSet.DutyCycleHighPluse > 2)
            {
                timerPWMSet.DutyCycleHighPluse--;
                TIMER_PWMSet();
                printf("DutyCycle = %d\r\n", timerPWMSet.DutyCycleHighPluse);
            }
        }
        break;
        case '0':
        {
            if (TIMM0->TIM[TIM_0].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_0, DISABLE);
                printf("TIMER0_0 PWM DISABLE\n");
            }
            else
            {
                //TIM_Cmd(TIMM0, TIM_0, ENABLE);
                printf("TIMER0_0 PWM ENABLE\n");
            }
        }
        break;
        case '1':
        {
            if (TIMM0->TIM[TIM_1].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_1, DISABLE);
                printf("TIMER0_1 PWM DISABLE\n");
            }
            else
            {
                TIM_Cmd(TIMM0, TIM_1, ENABLE);
                printf("TIMER0_1 PWM ENABLE\n");
            }
        }
        break;
        case '2':
        {
            if (TIMM0->TIM[TIM_2].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_2, DISABLE);
                printf("TIMER0_2 PWM DISABLE\n");
            }
            else
            {
                TIM_Cmd(TIMM0, TIM_2, ENABLE);
                printf("TIMER0_2 PWM ENABLE\n");
            }
        }
        break;
        case '3':
        {
            if (TIMM0->TIM[TIM_3].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_3, DISABLE);
                printf("TIMER0_3 PWM DISABLE\n");
            }
            else
            {
                TIM_Cmd(TIMM0, TIM_3, ENABLE);
                printf("TIMER0_3 PWM ENABLE\n");
            }
        }
        break;
        case '4':
        {
            if (TIMM0->TIM[TIM_4].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_4, DISABLE);
                printf("TIMER0_4 PWM DISABLE\n");
            }
            else
            {
                TIM_Cmd(TIMM0, TIM_4, ENABLE);
                printf("TIMER0_4 PWM ENABLE\n");
            }
        }
        break;
        case '5':
        {
            if (TIMM0->TIM[TIM_5].ControlReg & 0x01)
            {
                TIM_Cmd(TIMM0, TIM_5, DISABLE);
                printf("TIMER0_5 PWM DISABLE\n");
            }
            else
            {
                TIM_Cmd(TIMM0, TIM_5, ENABLE);
                printf("TIMER0_5 PWM ENABLE\n");
            }
        }
        break;

        //ESC
        case 0x1B:
        {
            PrintSet();
            PrintList();
        }
        break;
        }
    }
}

void PWM_Pin_Set(void)
{
    uint8_t index,i;
    printf("Please Input Pin index:(R:1/2/3/4) ");
    do
    {
        if (UART_GetLineStatus(UART0) & UART_LINE_STATUS_RX_RECVD)
        {
            index = UART_ReceiveData(UART0) - 48;
            if (index == 0x1B)  //esc
            {
                return;
            }
            printf("%d", index);
        }

    } while (index < 1 || index > 4);


    PWM_CurrentPin = index - 1;
    printf("PWM_CurrentPin %d\r\n", PWM_CurrentPin);
    for (i = 0; i < sizeof(Ramp_List)/sizeof(Ramp_List[0]); i++)
    {

        if (Ramp_List[i].RampPin[PWM_CurrentPin].GPIOx != NULL)
        {
            printf("GPIOx%08X\r\n", (Ramp_List[i].RampPin[PWM_CurrentPin].GPIOx) );
            printf("RampPin%08X\r\n", (Ramp_List[i].RampPin[PWM_CurrentPin].RampPin) );

            GPIO_PinRemapConfig(Ramp_List[i].RampPin[PWM_CurrentPin].GPIOx, \
                                Ramp_List[i].RampPin[PWM_CurrentPin].RampPin, \
                                GPIO_Remap_2);

        }
    }
    printf("GPIO ALT:%08X\r\n", GPIO->ALT[0]);
    PrintfCurrentTimerRemap();
    PrintList();
}

void PrintfCurrentTimerRemap(void)
{
    uint16_t i;
    printf("\r\n====PWM Current Pin====\r\n");
    for (i = 0; i < sizeof(Ramp_List)/sizeof(Ramp_List[0]); i++)
    {
        printf("%d.%s\t%s\t\r\n", i, Ramp_List[i].PwmName, Ramp_List[i].RampPin[PWM_CurrentPin].PinName);
    }
    printf("=====+++++++++++=====\r\n");
}

void PrintfTimerRemapList(void)
{
    uint16_t i;
    printf("\r\n==============Set Alt Pin==============\r\n");
    printf("   C\\R\t 1\t 2\t 3\t 4\r\n");
    for (i = 0; i < sizeof(Ramp_List)/sizeof(Ramp_List[0]); i++)
    {
        printf("%d.%s\t%s\t%s\t%s\t%s\r\n", i, Ramp_List[i].PwmName, Ramp_List[i].RampPin[0].PinName, Ramp_List[i].RampPin[1].PinName, \
               Ramp_List[i].RampPin[2].PinName, Ramp_List[i].RampPin[3].PinName);
    }
    printf("==============+++++++++++==============\r\n");
}

void UART_Configuration(void)
{
    UART_InitTypeDef UART_InitStructure;

    GPIO_PinRemapConfig(GPIOA, GPIO_Pin_0 | GPIO_Pin_1, GPIO_Remap_0);

    UART_InitStructure.UART_BaudRate = 115200;
    UART_InitStructure.UART_WordLength = UART_WordLength_8b;
    UART_InitStructure.UART_StopBits = UART_StopBits_1;
    UART_InitStructure.UART_Parity = UART_Parity_No;

    UART_Init(UART0, &UART_InitStructure);
}

void TimerPWMSetStructInit()
{
    timerPWMSet.PWM_HZ = 1000;
    timerPWMSet.DutyCycleHighPluse = 1;
    timerPWMSet.DutyCycleLowPluse = 1;

    SYSCTRL_GetClocksFreq(&timerPWMSet.clocks);
    if(timerPWMSet.clocks.PCLK_Frequency < (timerPWMSet.PWM_HZ * 2))
    {
        printf("You set PWM frequency is %dHz.\n",timerPWMSet.PWM_HZ);
        printf("The highest frequency of PWM is PCLK_Frequency/2,is %dHz.\n",timerPWMSet.clocks.PCLK_Frequency);

        while(1);
    }
}

void TIMER_PWMSet(void)
{
    uint32_t Period = 0;
    uint32_t DutyCyclePeriod = 0;

    Period = timerPWMSet.clocks.PCLK_Frequency / timerPWMSet.PWM_HZ;

    DutyCyclePeriod = Period / (timerPWMSet.DutyCycleLowPluse + timerPWMSet.DutyCycleHighPluse);

    timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod = (DutyCyclePeriod * timerPWMSet.DutyCycleLowPluse - 1);
    timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod = (DutyCyclePeriod * timerPWMSet.DutyCycleHighPluse - 1);

    TIM_SetPWMPeriod(TIMM0, TIM_0,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);
    TIM_SetPWMPeriod(TIMM0, TIM_1,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);
    TIM_SetPWMPeriod(TIMM0, TIM_2,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);
    TIM_SetPWMPeriod(TIMM0, TIM_3,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);
    TIM_SetPWMPeriod(TIMM0, TIM_4,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);
    TIM_SetPWMPeriod(TIMM0, TIM_5,
                     timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod,
                     timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod);

}

void TIMER_Configuration(void)
{
    uint32_t Period = 0;
    uint32_t DutyCyclePeriod = 0;

    Period = timerPWMSet.clocks.PCLK_Frequency / timerPWMSet.PWM_HZ;

    DutyCyclePeriod = Period / (timerPWMSet.DutyCycleLowPluse + timerPWMSet.DutyCycleHighPluse);

    timerPWMSet.TIM_PWMSetStruct.TIM_LowLevelPeriod = (DutyCyclePeriod * timerPWMSet.DutyCycleLowPluse - 1);
    timerPWMSet.TIM_PWMSetStruct.TIM_HighLevelPeriod = (DutyCyclePeriod * timerPWMSet.DutyCycleHighPluse - 1);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_0;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_1;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_2;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_3;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_4;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

    timerPWMSet.TIM_PWMSetStruct.TIMx = TIM_5;
    TIM_PWMInit(TIMM0, &timerPWMSet.TIM_PWMSetStruct);

}


void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

//	NVIC_SetVectorTable(NVIC_VectTab_RAM,0);
    NVIC_SetPriorityGrouping(NVIC_PriorityGroup_3);

    NVIC_InitStructure.NVIC_IRQChannel = UART0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = TIM0_0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);


}



#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

    /* Infinite loop */
    while (1)
    {
        printf("%s\t%s\t%s\r\n", __func__, file, line);
    }
}
#endif






